package com.gitee.feizns.quickstart.web.Interceptors.bucket4j;

import com.gitee.feizns.dynamic.reflect.Constructors;
import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.Caffeine;
import io.github.bucket4j.Bucket;
import lombok.RequiredArgsConstructor;
import org.springframework.lang.NonNull;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.time.Duration;
import java.util.Collections;
import java.util.List;

/**
 * 速率限制拦截器
 * @author feizns
 * @since 2025/10/28
 */
@RequiredArgsConstructor
public class CaffeineRateLimitInterceptor implements HandlerInterceptor {

    /**
     * 供应商
     */
    private final RateLimitHandler handler;

    /**
     * 匹配器
     */
    private final AntPathMatcher matcher = new AntPathMatcher();

    /**
     * 缓存
     */
    private final Cache<String, Bucket> cache = Caffeine.newBuilder().expireAfterAccess(Duration.ofHours(1)).build();

    /**
     * 获取 URL 模式
     * @return {@link List }<{@link String }>
     */
    public List<String> getUrlPatterns() {
        return Collections.emptyList();
    }

    @Override
    public boolean preHandle(@NonNull HttpServletRequest request,
                             @NonNull HttpServletResponse response,
                             @NonNull Object handler) throws Exception {
        if ( handler instanceof HandlerMethod ) {
            if ( getUrlPatterns().stream().anyMatch(url -> matcher.match(url, request.getRequestURI())) ) {
                HandlerMethod method = (HandlerMethod) handler;
                RateLimitHandler rateLimitHandler = getBucketHandler(method);
                Bucket bucket = cache.get(rateLimitHandler.key(), rateLimitHandler::bucket);
                if ( bucket != null )
                    rateLimitHandler.throwIfExceeded(bucket);
            }
        }
        return HandlerInterceptor.super.preHandle(request, response, handler);
    }

    /**
     * 获取桶
     * @param method 方法
     * @return {@link Bucket }
     */
    public RateLimitHandler getBucketHandler(HandlerMethod method) {
        RateLimit annotation = method.getMethodAnnotation(RateLimit.class);
        if ( annotation == null )
            annotation = method.getBeanType().getAnnotation(RateLimit.class);
        return annotation == null ? handler : Constructors.newInstance(annotation.value());
    }

}
